#include "./sensor.h"
#include "../device/gpio.h"
#include "../rtx/rtx_os.h"
#include "../service/config.h"

namespace driver {

using namespace device::gpio;
using namespace utils;

using SCK = PA<5>;
using MISO = PA<6>;
using MOSI = PA<7>;

using LEAK = PC<2>;
using STEAM3 = PC<4>;
using STEAM4 = PC<5>;
using FLOW = PA<1>;

constexpr uint16_t CN_STEAM = 0x8FE3; // CN1
constexpr uint16_t CN_WATER = 0xBFE3; 
constexpr uint16_t CN_TM = 0x81F3;

constexpr uint16_t NOT_FOUND = static_cast<uint16_t>(-1);
static uint16_t find_by_ad(uint16_t val);

Sensor::Sensor() {

    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN + 
        RCC_APB2ENR_SPI1EN +
        RCC_APB2ENR_IOPCEN;
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;

    config_float_input<LEAK, STEAM3, STEAM4>();

    config_af_pp_out<SCK, MOSI>();
    config_float_input<MISO>();

    SPI1->CR1 = 0x00;
    SPI1->CR2 = 0x00;
    SPI1->CR1 = 0x0300 + 0x800 + 0x40 + 0x05 + (0x05 << 3);

    TIM2->CR1 = 0x00;
    TIM2->CR2 = 0x00;
    TIM2->SMCR = 0x67;
    TIM2->CCER = 0x0000;
    TIM2->CCMR1 = 0x0100;
    TIM2->CR1 = 0x01;
}

bool Sensor::is_leak() {
    return LEAK::read();
}

bool Sensor::is_steam_high() {
    return STEAM3::read();
}

bool Sensor::is_steam_low() {
    return STEAM4::read();
}

void Sensor::flow_reset() {
    TIM2->CNT = 0;
}

uint16_t Sensor::flow_count() {
    return TIM2->CNT;
}

uint16_t Sensor::flow_volume() {
    using namespace service;
    return TIM2->CNT * 1000 / config->flow1000_count;
}

static uint16_t spi_wr(uint16_t val) {
	while (0x00 == (SPI1->SR & SPI_SR_TXE));
	SPI1->DR = val;
	while (0x00 == (SPI1->SR & SPI_SR_RXNE));
	return SPI1->DR;
}

static void read_chan(uint16_t chan, uint16_t &val) {
    osDelay(100);
    spi_wr(chan);
    osDelay(100);
    val = spi_wr(chan);
}

static void read_tm(int16_t &value) {
    uint16_t tmp = 1;
    read_chan(CN_TM, tmp);
    if (tmp & 0x8000) {
        value = - ((0x10000 - tmp) >> 7);
    } else {
        value = tmp >> 7;
    }
}

static Error read_temp(uint16_t chan, int16_t tm, int16_t &value) {
    uint16_t temp;
    read_chan(chan, temp);
    if (temp & 0x8000) {
        uint16_t index = find_by_ad((0x10000 - temp) * 1000 * 256 / 32768);
        if (index == NOT_FOUND) {
            return ec::TEMP_FAIL;
        }
        value = -index + tm;
    } else {
        uint16_t index = find_by_ad(temp * 1000 * 256 / 32768);
        if (index == NOT_FOUND) {
            return ec::TEMP_FAIL;
        }
        value = index + tm;
    }
    return ec::OK;
}

void Sensor::read_all_temp() {
    int16_t tm;
    read_tm(tm);
   // m_water_status = read_temp(CN_WATER, tm, m_water_temp);
    m_steam_status = read_temp(CN_STEAM, tm, m_steam_temp);
}

const uint16_t TEMP_DATA[] = {
    0,      397,    798,    1203,   1611,   2022,   2436,   2850,   3266,   3681,   
    4095,   4508,   4919,   5327,   5733,   6137,   6539,   6939,   7338,   7737,   
    8137,   8537,   9341,   9745,   10151,  10560,  10969,  11381,  11793
};

constexpr uint32_t TEMP_LIST_COUNT = sizeof (TEMP_DATA) / sizeof (TEMP_DATA[0]);

static uint16_t find_by_ad(uint16_t val) {
    for (uint32_t i = 0; i < (TEMP_LIST_COUNT - 1); ++ i) {
        if ((val >= TEMP_DATA[i]) && (val <= TEMP_DATA[i + 1])) {
            return i * 10 + (val - TEMP_DATA[i]) * 10 / (TEMP_DATA[i + 1] - TEMP_DATA[i]);
        }
    }
    return NOT_FOUND;
}

}
